﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using FXnaInfrastructure;

using DotSpatial.Topology;

using Point = System.Drawing.Point;

using System.Drawing;


namespace FXnaGis
{
    [Serializable]
    /// <summary>
    /// 这里的类型被作为这种单元格方法等值线绘制的标准方法
    /// 
    /// </summary>
   public class FContourShape:FShape
    {


       public FContourShape(string id,FGrid grid)
           : base(id)
       {


           this._grid = grid;


          
 

       }



       FGrid _grid;

       public FGrid Grid
       {
           get { return _grid; }
           set { _grid = value; }
       }



       double[,] _data;

       public double[,] Data
       {
           get { return _data; }
           set {
               
               _data = value;

               this.BuildContourLines();
           
           }
       }



       List<ContourLine> _contourlines = new List<ContourLine>();




       /// <summary>
       /// 对外的接口，用于控制生成哪几条线，以及对应的颜色等
       /// </summary>
       public void SetContourLines(List<ContourLine> lines)
       {
           if (lines!=null)
           {
               this._contourlines.Clear();

               this._contourlines.AddRange(lines);

               //重新提取线模型

               this.BuildContourLines();

               
           }

          




       }


   



       /// <summary>
       /// 内部绘制直线的保存
       /// 先这样调节等值线，之后在考虑填充事宜
       /// </summary>

       List<FContourLine> _drawLines = new List<FContourLine>();



     




    

       public override DotSpatial.Data.Extent Extent
       {
           get
           {
               return new DotSpatial.Data.Extent(this._grid.XMin, this._grid.YMin, this._grid.XMax, this._grid.YMax);
           }
       }




       public override void Draw(FMapArgs args)
       {
         


           //划线的代码


           foreach (FContourLine line in this._drawLines)
           {

               Point p0;
               Point p1;


               Pen p = new Pen(line.Color);

               foreach (LineSegment item in line.Lines)
               {
                   p0 = args.ProjToPixel(item.P0);
                   p1 = args.ProjToPixel(item.P1);

                   

                   args.Device.DrawLine(p, p0, p1);


               }



               p.Dispose();




               
           }




          //准备填充面的代码

           List<PointF> poly=new List<PointF> ();


           foreach (FContourLine line in this._drawLines)
           {

               SolidBrush sb = new SolidBrush(line.Color);

              

               foreach (LinearRing item in line.Polys)
               {

                   poly.Clear();


                   for (int i = 0; i < item.Count; i++)
                   {
                      Coordinate c=  item.Coordinates[i];


                      PointF p = args.ProjToPixel(c);

                      poly.Add(p);
                       
                   }



                   args.Device.FillPolygon(sb, poly.ToArray(), System.Drawing.Drawing2D.FillMode.Winding);




               }


            

               sb.Dispose();
           }
           




         


       }

       public override bool Intersects(DotSpatial.Topology.Envelope envelope)
       {
         

           if (this.Extent.Intersects(envelope))
           {
               return true;

           }
           else
           {
               return false;
           }



          
       }



       protected FContourLine _currentLine;
   


       protected void BuildContourLines()
       {

           ///防止 空值 错误

           if (this._data==null)
           {
               return;
               
           }



           //去掉重复项

          

           List<double> lstDouble = new List<double>();

           foreach (ContourLine cl in this._contourlines)
           {
               if (!lstDouble.Contains(cl.Value))
               {

                   lstDouble.Add(cl.Value);

                   
               }

              
               
           }


           //排序

           lstDouble.Sort();



           this._drawLines.Clear();


           foreach (double item in lstDouble)
           {
               ContourLine line = this._contourlines.First<ContourLine>(x => x.Value == item);




               FContourLine cline = new FContourLine(line);

               this._currentLine = cline;

               this.BuildLine(line.Value);


               //这里的逻辑有点复杂了，哈哈
               //以后会改，这里分为对外用户设定，以及对内绘制线条
               this._drawLines.Add(this._currentLine);

               
           }




         


       }




      




       protected void BuildLine(double threshold)
       {

           //取得所有的矩形块

           for (int i = 0; i < this._grid.NX - 1; i++)
           {
               for (int j = 0; j < this._grid.NY - 1; j++)
               {

                   

                   //这里真的取反了，应该是逆时针旋转的坐标，千万记住啊


                   double a = this._data[i, j];
                   double b = this._data[i+1, j];
                   double c = this._data[i + 1, j + 1];
                   double d = this._data[i, j+1];




                 



                  double dx = this._grid.GetXPos(i + 1) - this._grid.GetXPos(i);
                  double dy = this._grid.GetYPos(j + 1) - this._grid.GetYPos(j);

                  double ox = this._grid.GetXPos(i);
                  double oy = this._grid.GetYPos(j);




                   //在之后的改进中，加检查逻辑多里出来

                   //这里需要加入判断逻辑，终于找到问题的关键了
                   //如果用户输入的数据，有问题，比如double.nan,比如用户指定的无效值，等，都需要在这里执行过滤


                  if (double.IsNaN(a) || double.IsNaN(b) || double.IsNaN(c) || double.IsNaN(d))
                  {
                      return;

                  }








                   //进入矩形循环过程，

                   //感觉自己好伟大啊，这种代码都能想出来，哈哈哈哈哈哈

                  this.BuildRectangle(ox,oy,dx,dy,a,b,c,d,threshold);





                 





               }

           }




       }




       /// <summary>
       /// 这个函数将负责构建一个矩形，用于完成迭代过程
       /// 当存在二义性时，需要迭代自身
       /// </summary>
       /// <param name="ox"></param>
       /// <param name="oy"></param>
       /// <param name="dx"></param>
       /// <param name="dy"></param>
       /// <param name="a"></param>
       /// <param name="b"></param>
       /// <param name="c"></param>
       /// <param name="d"></param>
       /// <param name="threshold"></param>
       protected void BuildRectangle(double ox,double oy,double dx,double dy,double a,double b,double c,double d,double threshold)
       {


           int n = this.cell(a, b, c, d, threshold);


           switch (n)
           {
               case 1:
               case 2:
               case 4:
               case 7:
               case 8:
               case 11:
               case 13:
               case 14:
                   //等高线，切掉一个角点

                  // this.Draw_One(n, ox, oy, dx, dy, a, b, c, d, threshold);

                

                   break;

               case 3:
               case 6:
               case 9:
               case 12:

                   //等高线横穿整个单元
                   

                   this.Draw_adjacent(n,ox,oy,dx,dy,a,b,c,d,threshold);

                   break;


               case 5:
               case 10:
                   //二义性
                   //这里需要重新划分坐标，然后继续执行过程

                   //重新计算矩形中心点

                   double cx = ox + 0.5 * dx;
                   double cy = oy + 0.5 * dy;

                   //计算矩形中心对应的值,这里我们应用一个简单的线性差值
                   //想来想去，还是应用最简单的方法吧

                   double e = (a + b + c + d) * 0.25;

                   double hab = (a + b) * 0.5;
                   double hbc = (b + c) * 0.5;

                   double hcd = (c + d) * 0.5;
                   double hda = (d + a) * 0.5;



                   //之后已经确定分为四个矩形
                   //网格四分是顺时针，
                   //abcd取值 逆时针方向

                   this.BuildRectangle(ox,oy,0.5*dx,0.5*dy,a,hab,e,hda,threshold);
                   this.BuildRectangle(ox, cy, 0.5 * dx, 0.5 * dy,hda,e,hcd,d,threshold);
                   this.BuildRectangle(cx, cy, 0.5 * dx, 0.5 * dy,e,hbc,c,hcd,threshold);
                   this.BuildRectangle(cx,oy,0.5*dx,0.5*dy,hab,b,hbc,e,threshold);

                   //下面的函数永不需要，按照资料，这里不会形成死循环

                   //this.Draw_opposite(n, i, j, a, b, c, d, threshold);

                   break;




               case 0:

                   //无等高线

                  

                   break;


               case 15:


                   //无等高线
                   //但是需要计算是否填充

                      Coordinate c1 = new Coordinate(ox, oy);
                    Coordinate c2 = new Coordinate(ox + dx, oy);
                    Coordinate c3 = new Coordinate(ox + dx, oy + dy);
                    Coordinate c4 = new Coordinate(ox, oy + dy);

                    LinearRing lr = new LinearRing(new List<Coordinate>() { c1,c2,c3,c4});

                    this._currentLine.AddRing(lr);


                   break;

               default:
                   break;
           }


 
       }


       /// <summary>
       /// a,b,c,d为四个顶点值
       /// 本算法基于清华大学出版社 edward angle 的交互计算机图形学一书
       /// </summary>
       /// <param name="a"></param>
       /// <param name="b"></param>
       /// <param name="c"></param>
       /// <param name="d"></param>
       /// <param name="threshold">等高线值</param>
       /// <returns></returns>
       int cell(double a, double b, double c, double d, double threshold)
       {
           int n = 0;
           if (a > threshold) n += 1;
           if (b > threshold) n += 8;
           if (c > threshold) n += 4;
           if (d > threshold) n += 2;



           return n;

       }


       /// <summary>
       /// 最简单切掉一个角的模型
       /// </summary>
       /// <param name="num"></param>
       /// <param name="i"></param>
       /// <param name="j"></param>
       /// <param name="a"></param>
       /// <param name="b"></param>
       /// <param name="c"></param>
       /// <param name="d"></param>
       /// <param name="threshold"></param>
       protected void Draw_One(int num, double ox,double oy,double dx,double dy, double a, double b, double c, double d, double threshold)
       {

           //num 单元类型 i,j单元在单元阵列中的位置 abcd 4个顶点值，左下角顶点开始，逆时针

           //s_x s_y 单元x ,y方向的长度

           //ox ,oy 坐下角坐标


           double x1 = 0, y1 = 0, x2 = 0, y2 = 0;

         


        


           switch (num)
           {

                   //左下
               case 1:
               case 14:
                   x1 = ox;
                   y1 = oy + dy * (threshold - a) / (d - a);
                   x2 = ox + dx * (threshold - a) / (b - a);
                   y2 = oy;
                   break;

                   //左上
               case 2:
               case 13:
                   x1 = ox;
                   y1 = oy + dy * (threshold - a) / (d - a);
                   x2 = ox + dx * (threshold - d) / (c - d);
                   y2 = oy + dy;
                   break;

                   //右上
               case 4:
               case 11:
                   x1 = ox + dx * (threshold - d) / (c - d);
                   y1 = oy + dy;
                   x2 = ox + dx;
                   y2 = oy + dy * (threshold - b) / (c - b);
                   break;

                   //右下

               case 7:
               case 8:
                   x1 = ox + dx * (threshold - a) / (b - a);
                   y1 = oy;
                   x2 = ox + dx;
                   y2 = oy + dy * (threshold - b) / (c - b);
                   break;
           }









           LineSegment ls = new LineSegment(new Coordinate(x1, y1), new Coordinate(x2, y2));



           //临时的代码，以后会替换掉

           this._currentLine.Add(ls);

           //this._lines.Add(ls);



           //根据不同的方法进行填充着色的代码
           //目前以切白色为基础，先试验一下


           LinearRing lr = null;

           Coordinate c1 = new Coordinate(ox,oy);
           Coordinate c2 = new Coordinate(ox+dx, oy);
           Coordinate c3 = new Coordinate(ox+dx, oy+dy);
           Coordinate c4 = new Coordinate(ox, oy+dy);



           switch (num)
           {
               case 1:

                   //做下角点

                   lr = new LinearRing(new List<Coordinate> { new Coordinate(x1, y1), c1, new Coordinate(x2, y2) });

                  


                   break;


               case 14:


                   //以逆时针为序，做测试

                   lr = new LinearRing(new List<Coordinate> { new Coordinate(x1, y1), new Coordinate(x2, y2), c2, c3, c4 });
                  

                   break;


               case 2:

                   lr = new LinearRing(new List<Coordinate> { new Coordinate(x1, y1), c4, new Coordinate(x2, y2) });


                 

                   break;


               case 13:

                   lr = new LinearRing(new List<Coordinate> { new Coordinate(x1, y1), new Coordinate(x2, y2), c3, c2, c1 });

                   break;



               case 4:

                   lr = new LinearRing(new List<Coordinate> { new Coordinate(x1, y1), c3, new Coordinate(x2, y2) });

                   break;

               case 11:

                
                   lr = new LinearRing(new List<Coordinate> { c4, c1, c2, new Coordinate(x2, y2), new Coordinate(x1, y1) });


                   break;



               case 7:

                   lr = new LinearRing(new List<Coordinate> { c1, new Coordinate(x1, y1), new Coordinate(x2, y2) ,c3,c4 });

                   break;


               case 8:

                  

                   lr = new LinearRing(new List<Coordinate> { new Coordinate(x1, y1), c2, new Coordinate(x2, y2) });

                   break;
                  

           }


           if (lr!=null)
           {


               this._currentLine.AddRing(lr);
               
           }






       }




       protected void Draw_adjacent(int num,double ox,double oy, double dx,double dy, double a, double b, double c, double d, double threshold)
       {

           double x1 = 0, y1 = 0, x2 = 0, y2 = 0;


          
       



           switch (num)
           {

                   //纵向切分
               case 3:
               case 12:

                   //这里需要考虑零除问题
                   //可是现在我已经忘记了这个代码的含义，这里先将就一下吧，之后再改

                   if (b != a)
                   {

                       x1 = ox + dx * (threshold - a) / (b - a);


                   }
                   else
                   {
                       x1 = ox;
 

                   }


                   
                   y1 = oy;


                   if (c != d)
                   {
                       x2 = ox + dx * (threshold - d) / (c - d);

                   }
                   else
                   {
                       x2 = ox;
 
                   }

                   
                   y2 = oy + dy;
                   break;

                   //横向切分

               case 6:
               case 9:
                   x1 = ox;

                   if (d != a)
                   {

                       y1 = oy + dy * (threshold - a) / (d - a);


                   }
                   else
                   {
                       y1 = oy;
                   }

                  
                   x2 = ox + dx;

                   if (c != b)
                   {

                       y2 = oy + dy * (threshold - b) / (c - b);


                   }
                   else
                   {
                       y2 = oy;
                   }
                  
                   break;
           }



          
         





           LineSegment ls = new LineSegment(new Coordinate(x1, y1), new Coordinate(x2, y2));


           this._currentLine.Add(ls);
           //this._lines.Add(ls);



           //测试填充多边形
           LinearRing lr = null;

           Coordinate c1 = new Coordinate(ox, oy);
           Coordinate c2 = new Coordinate(ox + dx, oy);
           Coordinate c3 = new Coordinate(ox + dx, oy + dy);
           Coordinate c4 = new Coordinate(ox, oy + dy);


           Coordinate o1 = new Coordinate(x1,y1);
           Coordinate o2 = new Coordinate(x2,y2);

           switch (num)
           {

               case 3:

                   lr = new LinearRing(new List<Coordinate>() { c1,o1,o2,c4 });

                   break;

               case 12:

                   lr = new LinearRing(new List<Coordinate>() { o1, o2, c3,c2 });

                   break;




               case 6:

                   lr = new LinearRing(new List<Coordinate>() { o1, o2, c3, c4 });

                   break;


               case 9:
                   lr = new LinearRing(new List<Coordinate>() { o1, o2, c2, c1 });

                   break;




           }




           if (lr != null)
           {


               this._currentLine.AddRing(lr);

           }




       }




      



    }
}
